'use client';

import { useState, useEffect, useCallback, useRef } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { useAuth } from '@/lib/auth-context';
import { projectsApi, assetsApi, invitationsApi, Project, Asset, Invitation, TranscodeStatus } from '@/lib/api';
import { useDropzone } from 'react-dropzone';
import { TranscodeTasksPanel } from '@/components/transcode/TranscodeTasksPanel';

async function safeCopy(text: string): Promise<void> {
  if (typeof window === 'undefined') return;
  if (navigator.clipboard?.writeText) {
    try { await navigator.clipboard.writeText(text); } catch { /* ignore */ }
  } else {
    // Fallback: create a temp input so we can use execCommand on insecure contexts
    const el = document.createElement('textarea');
    el.value = text;
    el.style.cssText = 'position:fixed;top:-999px;left:-999px;opacity:0';
    document.body.appendChild(el);
    el.focus(); el.select();
    try { document.execCommand('copy'); } catch { /* ignore */ }
    document.body.removeChild(el);
  }
}

const ROLE_COLORS: Record<string, string> = {
  ADMIN:   'badge-danger',
  EDITOR:  'badge-brand',
  REVIEWER:'badge-muted',
  VIEWER:  'badge-subtle',
};

const ROLE_LABELS: Record<string, string> = {
  ADMIN:   'Admin',
  EDITOR:  'Editor',
  REVIEWER:'Reviewer',
  VIEWER:  'Viewer',
};

export default function ProjectDetailPage() {
  const params = useParams();
  const projectId = params.projectId as string;
  const { user, token } = useAuth();
  const router = useRouter();

  const [project, setProject] = useState<Project | null>(null);
  const [members, setMembers] = useState<any[]>([]);
  const [pendingInvites, setPendingInvites] = useState<Invitation[]>([]);
  const [assets, setAssets] = useState<Asset[]>([]);
  const [loading, setLoading] = useState(true);
  const [uploading, setUploading] = useState(false);
  const [activeTab, setActiveTab] = useState<'videos' | 'members' | 'transcode'>('videos');

  // Invite form state (single shared form)
  const [inviteEmail, setInviteEmail] = useState('');
  const [inviteRole, setInviteRole] = useState('REVIEWER');
  const [inviting, setInviting] = useState(false);
  const [inviteError, setInviteError] = useState('');
  const [inviteSuccess, setInviteSuccess] = useState('');
  const [createdLink, setCreatedLink] = useState('');

  // Edit member role
  const [editingRoleId, setEditingRoleId] = useState<string | null>(null);
  const [editingRole, setEditingRole] = useState('');
  const [updatingRole, setUpdatingRole] = useState(false);

  // Remove member
  const [confirmRemove, setConfirmRemove] = useState<{ id: string; name: string } | null>(null);
  const [removing, setRemoving] = useState(false);

  // Revoke invite
  const [revokingId, setRevokingId] = useState<string | null>(null);

  // Copy link
  const [copiedInviteId, setCopiedInviteId] = useState<string | null>(null);
  const [inviteUrlMap, setInviteUrlMap] = useState<Record<string, string>>({});

  const canManage = members.some(m =>
    m.user.id === user?.id && ['ADMIN', 'EDITOR'].includes(m.role)
  );
  const isAdmin = members.some(m =>
    m.user.id === user?.id && m.role === 'ADMIN'
  );

  const loadAll = useCallback(async () => {
    if (!token) return;
    try {
      const [{ project: p }, { assets: a }] = await Promise.all([
        projectsApi.get(token, projectId),
        assetsApi.list(token, projectId),
      ]);
      setProject(p);
      setMembers(p.members ?? []);
      setAssets(a);

      if (canManage) {
        const { invitations } = await invitationsApi.list(token, projectId);
        setPendingInvites(invitations.filter((i: Invitation) => i.status === 'PENDING'));
      }
    } catch {
      router.push('/projects');
    } finally {
      setLoading(false);
    }
  }, [token, projectId, router, canManage]);

  useEffect(() => { loadAll(); }, [loadAll]);

  // ── Invite member ──────────────────────────────────────────────────────────
  const handleInvite = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!token || !inviteEmail.trim()) return;
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(inviteEmail.trim())) {
      setInviteError('Invalid email address');
      return;
    }
    setInviting(true);
    setInviteError('');
    setInviteSuccess('');
    setCreatedLink('');
    try {
      const { inviteUrl } = await invitationsApi.create(token, projectId, inviteEmail.trim(), inviteRole);
      const { invitations } = await invitationsApi.list(token, projectId);
      setPendingInvites(invitations.filter((i: Invitation) => i.status === 'PENDING'));
      setInviteUrlMap(prev => ({ ...prev, [inviteUrl.split('/').pop()!]: inviteUrl }));
      setInviteEmail('');
      setInviteSuccess(`Invitation sent to ${inviteEmail.trim()}`);
      setTimeout(() => setInviteSuccess(''), 3000);
    } catch (err) {
      setInviteError(err instanceof Error ? err.message : 'Failed to send invitation');
    } finally {
      setInviting(false);
    }
  };

  // ── Create & copy link ─────────────────────────────────────────────────────
  const handleCreateLink = async () => {
    if (!token || !inviteEmail.trim()) return;
    setInviting(true);
    setInviteError('');
    setInviteSuccess('');
    setCreatedLink('');
    try {
      const { inviteUrl } = await invitationsApi.create(token, projectId, inviteEmail.trim(), inviteRole);
      const { invitations } = await invitationsApi.list(token, projectId);
      setPendingInvites(invitations.filter((i: Invitation) => i.status === 'PENDING'));
      // API returns full URL now (e.g. http://localhost:3000/invite/xxx)
      await safeCopy(inviteUrl);
      setCreatedLink(inviteUrl);
      setInviteEmail('');
    } catch (err: any) {
      const msg = err instanceof Error ? err.message : String(err);
      if (msg.includes('already exists') || msg.includes('already a member') || msg.includes('409')) {
        setInviteError(`An invitation for "${inviteEmail.trim()}" is already pending or the user is already a member.`);
      } else {
        setInviteError(msg || 'Failed to create invitation link');
      }
    } finally {
      setInviting(false);
    }
  };

  // ── Change role ────────────────────────────────────────────────────────────
  const handleChangeRole = async (memberId: string) => {
    if (!token || !editingRole) return;
    setUpdatingRole(true);
    try {
      await projectsApi.updateMember(token, projectId, memberId, editingRole);
      setMembers(prev => prev.map(m => m.id === memberId ? { ...m, role: editingRole } : m));
      setEditingRoleId(null);
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to update role');
    } finally {
      setUpdatingRole(false);
    }
  };

  // ── Remove member ─────────────────────────────────────────────────────────
  const handleRemoveMember = async () => {
    if (!token || !confirmRemove) return;
    setRemoving(true);
    try {
      await projectsApi.removeMember(token, projectId, confirmRemove.id);
      setMembers(prev => prev.filter(m => m.id !== confirmRemove!.id));
      setConfirmRemove(null);
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to remove member');
    } finally {
      setRemoving(false);
    }
  };

  // ── Revoke invite ──────────────────────────────────────────────────────────
  const handleRevoke = async (invitationId: string) => {
    if (!token) return;
    setRevokingId(invitationId);
    try {
      await invitationsApi.revoke(token, invitationId);
      setPendingInvites(prev => prev.filter(i => i.id !== invitationId));
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to revoke invitation');
    } finally {
      setRevokingId(null);
    }
  };

  // ── Copy invite link ──────────────────────────────────────────────────────
  const handleCopyLink = async (invite: Invitation) => {
    const base = window.location.origin;
    const url = inviteUrlMap[invite.token] ?? `${base}/invite/${invite.token}`;
    await safeCopy(url);
    setCopiedInviteId(invite.id);
    setTimeout(() => setCopiedInviteId(null), 2000);
  };

  const handleDrop = async (acceptedFiles: File[]) => {
    if (!token || acceptedFiles.length === 0) return;
    setUploading(true);
    for (const file of acceptedFiles) {
      const formData = new FormData();
      formData.append('video', file);
      formData.append('projectId', projectId);
      formData.append('title', file.name.replace(/\.[^.]+$/, ''));
      try {
        const result = await assetsApi.upload(token, formData) as { asset: Asset };
        setAssets(prev => [result.asset, ...prev]);
      } catch (err) {
        console.error('Upload failed:', err);
        alert(`Upload failed: ${file.name}`);
      }
    }
    setUploading(false);
  };

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop: handleDrop,
    accept: { 'video/*': ['.mp4', '.mov', '.webm', '.avi', '.mpeg'] },
    multiple: true,
    disabled: uploading,
  });

  const statusColors: Record<string, string> = {
    PENDING_REVIEW:    'status-pending',
    CHANGES_REQUESTED: 'status-changes',
    APPROVED:          'status-approved',
    REJECTED:          'status-rejected',
  };

  const statusLabels: Record<string, string> = {
    PENDING_REVIEW:    'Pending',
    CHANGES_REQUESTED: 'Changes',
    APPROVED:          'Approved',
    REJECTED:          'Rejected',
  };

  // ── Transcode status helpers ────────────────────────────────────────────────
  const transcodeColors: Record<TranscodeStatus, { text: string; dot: string; bg: string }> = {
    PENDING:           { text: '#94A3B8', dot: 'bg-slate-400',    bg: 'rgba(148,163,184,0.10)' },
    UPLOADING:         { text: '#60A5FA', dot: 'bg-blue-400',     bg: 'rgba(96,165,250,0.10)'  },
    PROCESSING:        { text: '#A78BFA', dot: 'bg-violet-400',    bg: 'rgba(167,139,250,0.10)' },
    COMPLETED:         { text: '#34D399', dot: 'bg-emerald-400',   bg: 'rgba(52,211,153,0.10)'  },
    FAILED:            { text: '#F87171', dot: 'bg-red-400',       bg: 'rgba(248,113,113,0.10)' },
    UNSUPPORTED_CODEC: { text: '#FBBF24', dot: 'bg-amber-400',     bg: 'rgba(251,191,36,0.10)'  },
  };

  const transcodeLabels: Record<TranscodeStatus, string> = {
    PENDING:           'Queued',
    UPLOADING:         'Uploading',
    PROCESSING:        'Processing',
    COMPLETED:         'Ready',
    FAILED:            'Failed',
    UNSUPPORTED_CODEC: 'Unsupported codec',
  };

  // Poll for assets that are still processing
  const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);

  // ── Delete asset ─────────────────────────────────────────────────────────
  const [confirmDelete, setConfirmDelete] = useState<{ id: string; title: string } | null>(null);
  const [deletingId, setDeletingId] = useState<string | null>(null);

  const handleDeleteAsset = (id: string, title: string) => {
    setConfirmDelete({ id, title });
  };

  const confirmDeleteAsset = async () => {
    if (!token || !confirmDelete) return;
    setDeletingId(confirmDelete.id);
    try {
      await assetsApi.delete(token, confirmDelete.id);
      setAssets(prev => prev.filter(a => a.id !== confirmDelete.id));
      setConfirmDelete(null);
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to delete video');
    } finally {
      setDeletingId(null);
    }
  };
  useEffect(() => {
    const processingAssets = assets.filter(a =>
      ['UPLOADING', 'PROCESSING', 'PENDING'].includes(a.transcodeStatus)
    );
    if (processingAssets.length === 0) {
      if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; }
      return;
    }
    if (pollingRef.current) return; // already polling

    pollingRef.current = setInterval(async () => {
      if (!token) return;
      try {
        const { assets: updated } = await assetsApi.list(token, projectId);
        setAssets(updated);
      } catch {}
    }, 3000);

    return () => { if (pollingRef.current) clearInterval(pollingRef.current); };
  }, [token, projectId, assets]);

  if (loading) {
    return (
      <div className="min-h-screen flex items-center justify-center" style={{ background: 'var(--bg)' }}>
        <div className="flex items-center gap-3" style={{ color: 'var(--text-muted)' }}>
          <div className="w-5 h-5 rounded-full animate-spin"
               style={{ borderColor: '#6366F1', borderTopColor: 'transparent' }} />
          <span className="text-sm">Loading…</span>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen" style={{ background: 'var(--bg)' }}>

      {/* Header */}
      <header className="sticky top-0 z-10 px-8 py-4 flex items-center gap-5 shrink-0"
              style={{
                background: 'rgba(10,11,20,0.80)',
                backdropFilter: 'blur(12px)',
                borderBottom: '1px solid rgba(255,255,255,0.06)',
              }}>
        <button
          onClick={() => router.push('/projects')}
          className="flex items-center gap-1.5 text-sm transition-colors"
          style={{ color: 'var(--text-muted)' }}
        >
          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
          </svg>
          Projects
        </button>

        <div className="w-px h-4" style={{ background: 'rgba(255,255,255,0.10)' }} />

        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2">
            <h1 className="text-sm font-semibold truncate" style={{ color: 'var(--text)' }}>
              {project?.name}
            </h1>
            {canManage && (
              <span className="text-[10px] px-1.5 py-0.5 rounded-full shrink-0"
                    style={{ background: 'rgba(99,102,241,0.12)', color: '#A5B4FC' }}>
                {isAdmin ? 'Owner' : 'Editor'}
              </span>
            )}
            {!canManage && !isAdmin && (
              <span className="text-[10px] px-1.5 py-0.5 rounded-full shrink-0"
                    style={{ background: 'rgba(255,255,255,0.04)', color: 'var(--text-subtle)' }}>
                {members.find(m => m.user.id === user?.id)?.role ?? 'Member'}
              </span>
            )}
          </div>
          {project?.description && (
            <p className="text-xs truncate mt-0.5" style={{ color: 'var(--text-muted)' }}>
              {project.description}
            </p>
          )}
        </div>

        {/* Tabs */}
        <div className="flex items-center gap-1 p-1 rounded-lg"
             style={{ background: 'rgba(255,255,255,0.04)' }}>
          {[['videos', 'Videos', assets.length], ['transcode', 'Transcode Tasks', assets.filter(a => a.transcodeStatus !== 'COMPLETED').length], ['members', 'Members', members.length]].map(([tab, label, count]) => (
            <button key={tab}
              onClick={() => setActiveTab(tab as any)}
              className="px-3 py-1.5 rounded-md text-xs font-medium transition-all flex items-center gap-1.5"
              style={{
                background: activeTab === tab ? 'rgba(99,102,241,0.20)' : 'transparent',
                color: activeTab === tab ? '#A5B4FC' : 'var(--text-muted)',
              }}>
              {label}
              {tab !== 'members' && (count as number) > 0 && (
                <span className="text-[10px] px-1.5 py-0.5 rounded-full"
                      style={{
                        background: tab === 'transcode'
                          ? 'rgba(167,139,250,0.25)'
                          : 'rgba(255,255,255,0.06)',
                        color: tab === 'transcode' ? '#A78BFA' : 'inherit',
                      }}>
                  {count}
                </span>
              )}
              {tab === 'members' && (
                <span className="ml-0.5 text-[10px] px-1.5 py-0.5 rounded-full"
                      style={{ background: 'rgba(255,255,255,0.06)', color: 'inherit' }}>
                  {members.length}
                </span>
              )}
            </button>
          ))}
        </div>

        <div className="text-xs px-2.5 py-1 rounded-full"
             style={{ background: 'rgba(255,255,255,0.05)', color: 'var(--text-muted)' }}>
          {assets.length} video{assets.length !== 1 ? 's' : ''}
        </div>
      </header>

      <div className="px-8 py-6">

        {/* ── Videos Tab ───────────────────────────────────────────────────── */}
        {activeTab === 'videos' && (
          <>
            {/* Upload zone — only shown to EDITOR and ADMIN */}
            {canManage ? (
              <div
                {...getRootProps()}
                className="mb-8 rounded-2xl p-10 text-center cursor-pointer transition-all animate-fade-in"
                style={{
                  background: isDragActive ? 'rgba(99,102,241,0.08)' : 'rgba(255,255,255,0.02)',
                  border: isDragActive
                    ? '1px solid rgba(99,102,241,0.40)'
                    : '1px dashed rgba(255,255,255,0.10)',
                  borderRadius: '16px',
                }}
              >
                <input {...getInputProps()} />

                {uploading ? (
                  <div className="space-y-3">
                    <div className="w-9 h-9 rounded-full mx-auto animate-spin"
                         style={{ borderColor: '#6366F1', borderTopColor: 'transparent' }} />
                    <p className="text-sm" style={{ color: 'var(--text-muted)' }}>Uploading…</p>
                  </div>
                ) : (
                  <>
                    <div className="w-12 h-12 rounded-2xl mx-auto mb-4 flex items-center justify-center"
                         style={{ background: 'rgba(99,102,241,0.10)', border: '1px solid rgba(99,102,241,0.15)' }}>
                      <svg className="w-6 h-6" style={{ color: '#6366F1' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
                      </svg>
                    </div>
                    <p className="text-sm font-medium mb-1" style={{ color: 'var(--text)' }}>
                      {isDragActive ? 'Drop videos here' : 'Drag & drop videos, or click to browse'}
                    </p>
                    <p className="text-xs" style={{ color: 'var(--text-muted)' }}>
                      MP4, MOV, WebM — up to 500MB each
                    </p>
                  </>
                )}
              </div>
            ) : (
              <div className="mb-8 rounded-2xl p-6 text-center animate-fade-in"
                   style={{ background: 'rgba(255,255,255,0.01)', border: '1px solid rgba(255,255,255,0.05)', borderRadius: '16px' }}>
                <div className="w-10 h-10 rounded-2xl mx-auto mb-3 flex items-center justify-center"
                     style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.06)' }}>
                  <svg className="w-5 h-5" style={{ color: 'var(--text-subtle)' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                    <path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
                  </svg>
                </div>
                <p className="text-xs" style={{ color: 'var(--text-muted)' }}>
                  Your role ({members.find(m => m.user.id === user?.id)?.role ?? 'Member'}) does not allow uploading videos.
                </p>
              </div>
            )}

            {/* Asset grid */}
            {assets.length === 0 ? (
              <div className="text-center py-20 rounded-2xl animate-fade-in"
                   style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid rgba(255,255,255,0.06)' }}>
                <div className="w-14 h-14 rounded-2xl mx-auto mb-4 flex items-center justify-center"
                     style={{ background: 'rgba(99,102,241,0.08)', border: '1px solid rgba(99,102,241,0.12)' }}>
                  <svg className="w-7 h-7" style={{ color: '#6366F1' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.2}>
                    <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z" />
                  </svg>
                </div>
                <p className="text-sm font-medium mb-1" style={{ color: 'var(--text)' }}>No videos yet</p>
                <p className="text-xs" style={{ color: 'var(--text-muted)' }}>Upload your first video using the dropzone above</p>
              </div>
            ) : (
              <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
                {assets.map((asset, i) => (
                  <div key={asset.id}
                     className="card overflow-hidden group"
                     style={{ animation: `slideUp 0.25s ease-out ${i * 40}ms both` }}>

                    {/* Thumbnail */}
                    <div className="relative aspect-video" style={{ background: '#080810' }} onClick={() => router.push(`/review/${asset.id}`)}>
                      {/* Play overlay — only show when ready */}
                      {asset.transcodeStatus === 'COMPLETED' && (
                        <>
                          {asset.thumbnail ? (
                            <img
                              src={`/uploads/${asset.thumbnail}`}
                              alt={asset.title}
                              className="w-full h-full object-cover"
                              style={{ opacity: 0.85 }}
                            />
                          ) : (
                            <div className="w-full h-full flex items-center justify-center">
                              <svg className="w-10 h-10" style={{ color: 'rgba(255,255,255,0.15)' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
                                <path strokeLinecap="round" strokeLinejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                              </svg>
                            </div>
                          )}
                          <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200"
                               style={{ background: 'rgba(0,0,0,0.35)' }}>
                            <div className="w-12 h-12 rounded-full flex items-center justify-center"
                                 style={{ background: 'rgba(99,102,241,0.80)', boxShadow: '0 0 24px rgba(99,102,241,0.4)' }}>
                              <svg className="w-5 h-5 text-white ml-0.5" fill="currentColor" viewBox="0 0 24 24">
                                <path d="M8 5v14l11-7z" />
                              </svg>
                            </div>
                          </div>
                        </>
                      )}

                      {/* Not ready — show transcode status overlay */}
                      {asset.transcodeStatus !== 'COMPLETED' && (
                        <div className="absolute inset-0 flex flex-col items-center justify-center gap-3">
                          {/* Animated spinner */}
                          {['UPLOADING', 'PROCESSING', 'PENDING'].includes(asset.transcodeStatus) && (
                            <div className="w-10 h-10 rounded-full animate-spin"
                                 style={{ borderColor: transcodeColors[asset.transcodeStatus]?.dot.replace('bg-','#').replace('-400','' ) || '#6366F1', borderTopColor: 'transparent', borderWidth: '3px' }} />
                          )}
                          {/* Error icon */}
                          {asset.transcodeStatus === 'FAILED' && (
                            <div className="w-10 h-10 rounded-full flex items-center justify-center"
                                 style={{ background: 'rgba(248,113,113,0.15)' }}>
                              <svg className="w-5 h-5" style={{ color: '#F87171' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
                              </svg>
                            </div>
                          )}
                          {asset.transcodeStatus === 'UNSUPPORTED_CODEC' && (
                            <div className="w-10 h-10 rounded-full flex items-center justify-center"
                                 style={{ background: 'rgba(251,191,36,0.15)' }}>
                              <svg className="w-5 h-5" style={{ color: '#FBBF24' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
                              </svg>
                            </div>
                          )}

                          {/* Status label */}
                          <span className="text-xs font-medium px-2.5 py-1 rounded-full"
                                style={{ background: transcodeColors[asset.transcodeStatus]?.bg, color: transcodeColors[asset.transcodeStatus]?.text }}>
                            {transcodeLabels[asset.transcodeStatus]}
                          </span>
                        </div>
                      )}

                      {/* Progress bar */}
                      {['UPLOADING', 'PROCESSING'].includes(asset.transcodeStatus) && (
                        <div className="absolute bottom-0 left-0 right-0 h-1 overflow-hidden"
                             style={{ background: 'rgba(0,0,0,0.3)' }}>
                          <div
                            className="h-full transition-all duration-500"
                            style={{
                              width: `${asset.transcodeProgress}%`,
                              background: 'linear-gradient(90deg, #818CF8, #A78BFA)',
                            }}
                          />
                        </div>
                      )}

                      {/* Duration badge */}
                      {asset.duration && asset.transcodeStatus === 'COMPLETED' && (
                        <span className="absolute bottom-2 right-2 text-xs px-1.5 py-0.5 rounded-md font-mono"
                              style={{ background: 'rgba(0,0,0,0.70)', color: '#E2E8F0' }}>
                          {(() => { const m = Math.floor(asset.duration! / 60); const s = Math.floor(asset.duration! % 60); return `${m}:${s.toString().padStart(2,'0')}`; })()}
                        </span>
                      )}

                      {/* Codec badge */}
                      {asset.codec && asset.transcodeStatus !== 'COMPLETED' && (
                        <span className="absolute top-2 left-2 text-[10px] px-1.5 py-0.5 rounded font-mono"
                              style={{ background: 'rgba(0,0,0,0.6)', color: '#94A3B8' }}>
                          {asset.codec}
                        </span>
                      )}
                    </div>

                    {/* Info */}
                    <div className="p-4">
                      <div className="flex items-start justify-between gap-2 mb-2">
                        <h3 className="text-sm font-medium truncate flex-1 transition-colors"
                            style={{ color: 'var(--text)' }}>
                          {asset.title}
                        </h3>
                        <span className={`badge shrink-0 ${statusColors[asset.status]}`}>
                          {statusLabels[asset.status]}
                        </span>
                      </div>

                      {/* Transcode status row */}
                      {asset.transcodeStatus !== 'COMPLETED' && (
                        <div className="mb-2 flex items-center gap-1.5">
                          <div
                            className={`w-1.5 h-1.5 rounded-full shrink-0 ${['UPLOADING','PROCESSING'].includes(asset.transcodeStatus) ? 'animate-pulse' : ''} ${transcodeColors[asset.transcodeStatus]?.dot}`}
                          />
                          <span className="text-[11px] truncate" style={{ color: transcodeColors[asset.transcodeStatus]?.text }}>
                            {transcodeLabels[asset.transcodeStatus]}
                            {['UPLOADING', 'PROCESSING'].includes(asset.transcodeStatus) && asset.transcodeProgress > 0
                              ? ` — ${asset.transcodeProgress}%`
                              : ''}
                          </span>
                          {asset.transcodeStatus === 'FAILED' && asset.transcodeError && (
                            <span className="text-[10px] truncate" style={{ color: '#F87171' }}>
                              : {asset.transcodeError}
                            </span>
                          )}
                          {asset.transcodeStatus === 'UNSUPPORTED_CODEC' && (
                            <span className="text-[10px] truncate" style={{ color: '#FB923C' }}>
                              — will re-encode to H.264
                            </span>
                          )}
                        </div>
                      )}

                      <div className="flex items-center gap-2 text-xs" style={{ color: 'var(--text-muted)' }}>
                        <span>{(asset as any)._count?.comments ?? 0} comment{((asset as any)._count?.comments ?? 0) !== 1 ? 's' : ''}</span>
                        <span className="w-1 h-1 rounded-full" style={{ background: 'rgba(255,255,255,0.12)' }} />
                        <span>{new Date(asset.createdAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}</span>
                        <div className="flex-1" />
                        {canManage && (
                          <button
                            onClick={(e) => { e.stopPropagation(); handleDeleteAsset(asset.id, asset.title); }}
                            className="p-1 rounded transition-colors hover:bg-red-500/20 flex-shrink-0"
                            title="Delete video"
                          >
                            <svg className="w-3.5 h-3.5" style={{ color: '#F87171' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                              <path strokeLinecap="round" strokeLinejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
                            </svg>
                          </button>
                        )}
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </>
        )}

        {/* ── Transcode Tasks Tab ─────────────────────────────────────────── */}
        {activeTab === 'transcode' && (
          <div className="animate-fade-in">
            <TranscodeTasksPanel
              assets={assets}
              token={token}
              canManage={canManage}
              onDelete={handleDeleteAsset}
              onCancel={async (id) => {
                if (!token) return;
                try {
                  await assetsApi.cancelTranscode(token, id);
                  setAssets(prev => prev.map(a => a.id === id ? {
                    ...a,
                    transcodeStatus: 'PENDING',
                    transcodeProgress: 0,
                    transcodeError: null,
                    hlsPath: null,
                  } : a));
                } catch (err) {
                  alert(err instanceof Error ? err.message : 'Failed to cancel transcode');
                }
              }}
            />
          </div>
        )}

        {/* ── Members Tab ─────────────────────────────────────────────────── */}
        {activeTab === 'members' && (
          <div className="max-w-3xl animate-fade-in">

            {/* Invite form — single form, shared email + role */}
            {canManage && (
              <div className="card p-5 mb-6">
                <h2 className="text-sm font-semibold mb-4" style={{ color: 'var(--text)' }}>
                  Invite someone
                </h2>

                <div className="space-y-3">
                  <form
                    onSubmit={e => { e.preventDefault(); handleInvite(e); }}
                    className="flex items-end gap-3 flex-wrap"
                  >
                    <div className="flex-1 min-w-[180px]">
                      <label className="block text-xs mb-1.5" style={{ color: 'var(--text-muted)' }}>
                        Email address
                      </label>
                      <input
                        type="email"
                        className="input"
                        value={inviteEmail}
                        onChange={e => setInviteEmail(e.target.value)}
                        placeholder="colleague@company.com"
                      />
                    </div>
                    <div className="w-36">
                      <label className="block text-xs mb-1.5" style={{ color: 'var(--text-muted)' }}>Role</label>
                      <select
                        className="input"
                        value={inviteRole}
                        onChange={e => setInviteRole(e.target.value)}
                      >
                        {Object.entries(ROLE_LABELS).map(([value, label]) => (
                          <option key={value} value={value}>{label}</option>
                        ))}
                      </select>
                    </div>
                    {/* Both buttons share the same email + role from this single form */}
                    <button
                      type="button"
                      disabled={inviting || !inviteEmail.trim()}
                      onClick={handleCreateLink}
                      className="btn btn-secondary btn-md"
                      title="Create invite link and copy to clipboard"
                    >
                      {inviting ? 'Creating…' : (
                        <span className="flex items-center gap-1.5">
                          <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                            <path strokeLinecap="round" strokeLinejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" />
                          </svg>
                          Copy Link
                        </span>
                      )}
                    </button>
                    <button
                      type="submit"
                      disabled={inviting || !inviteEmail.trim()}
                      className="btn btn-primary btn-md"
                      title="Send invite — link is included automatically"
                    >
                      {inviting ? 'Sending…' : 'Send Invite'}
                    </button>
                  </form>

                  {/* Created link feedback */}
                  {createdLink && (
                    <div className="rounded-lg p-3 animate-scale-in"
                         style={{ background: 'rgba(34,197,94,0.08)', border: '1px solid rgba(34,197,94,0.20)' }}>
                      <div className="flex items-center gap-2 mb-1.5">
                        <svg className="w-3.5 h-3.5 shrink-0" style={{ color: '#86EFAC' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                          <path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                        </svg>
                        <span className="text-xs font-medium" style={{ color: '#86EFAC' }}>Link copied!</span>
                      </div>
                      <p className="text-[10px] break-all" style={{ color: 'rgba(134,239,172,0.7)' }}>
                        {createdLink}
                      </p>
                    </div>
                  )}

                  {inviteError && (
                    <p className="text-xs" style={{ color: '#F87171' }}>{inviteError}</p>
                  )}
                  {inviteSuccess && (
                    <p className="text-xs" style={{ color: '#86EFAC' }}>{inviteSuccess}</p>
                  )}
                </div>
              </div>
            )}

            {/* Members list */}
            <div className="card overflow-hidden mb-6">
              <div className="px-5 py-4 border-b" style={{ borderColor: 'rgba(255,255,255,0.06)' }}>
                <h2 className="text-sm font-semibold" style={{ color: 'var(--text)' }}>
                  Members ({members.length})
                </h2>
              </div>

              {members.length === 0 ? (
                <div className="p-8 text-center">
                  <p className="text-sm" style={{ color: 'var(--text-muted)' }}>No members yet</p>
                </div>
              ) : (
                <div className="divide-y" style={{ borderColor: 'rgba(255,255,255,0.04)' }}>
                  {members.map(m => {
                    const isMe = m.user.id === user?.id;
                    const canEdit = isAdmin && !isMe;

                    return (
                      <div key={m.id}
                           className="flex items-center gap-4 px-5 py-4 hover:bg-white/[0.02] transition-colors">

                        {/* Avatar */}
                        <div className="w-9 h-9 rounded-full flex items-center justify-center text-xs font-semibold shrink-0"
                             style={{ background: 'rgba(99,102,241,0.15)', color: '#A5B4FC' }}>
                          {m.user.name.split(' ').map((n: string) => n[0]).slice(0, 2).join('').toUpperCase()}
                        </div>

                        {/* Info */}
                        <div className="flex-1 min-w-0">
                          <div className="flex items-center gap-2">
                            <span className="text-sm font-medium" style={{ color: 'var(--text)' }}>
                              {m.user.name}
                              {isMe && <span className="ml-1.5 text-[10px]" style={{ color: 'var(--text-subtle)' }}>(you)</span>}
                            </span>
                          </div>
                          <p className="text-xs truncate" style={{ color: 'var(--text-muted)' }}>{m.user.email}</p>
                        </div>

                        {/* Joined date */}
                        <span className="text-xs hidden sm:block" style={{ color: 'var(--text-subtle)' }}>
                          {new Date(m.joinedAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
                        </span>

                        {/* Role */}
                        {editingRoleId === m.id ? (
                          <div className="flex items-center gap-2 shrink-0">
                            <select
                              className="input text-xs py-1.5"
                              value={editingRole}
                              onChange={e => setEditingRole(e.target.value)}
                              autoFocus
                            >
                              {Object.entries(ROLE_LABELS).map(([v, l]) => (
                                <option key={v} value={v}>{l}</option>
                              ))}
                            </select>
                            <button
                              onClick={() => handleChangeRole(m.id)}
                              disabled={updatingRole}
                              className="btn btn-primary btn-sm px-2"
                              title="Save"
                            >
                              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                              </svg>
                            </button>
                            <button
                              onClick={() => setEditingRoleId(null)}
                              className="btn btn-secondary btn-sm px-2"
                              title="Cancel"
                            >
                              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
                              </svg>
                            </button>
                          </div>
                        ) : (
                          <div className="flex items-center gap-2 shrink-0">
                            <span className={`badge ${ROLE_COLORS[m.role] ?? 'badge-muted'}`}>
                              {ROLE_LABELS[m.role] ?? m.role}
                            </span>
                            {canEdit && (
                              <button
                                onClick={() => { setEditingRoleId(m.id); setEditingRole(m.role); }}
                                className="btn btn-secondary btn-sm"
                                title="Change role"
                              >
                                <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                  <path strokeLinecap="round" strokeLinejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125" />
                                </svg>
                              </button>
                            )}
                            {isAdmin && !isMe && (
                              <button
                                onClick={() => setConfirmRemove({ id: m.user.id, name: m.user.name })}
                                className="btn btn-danger btn-sm"
                                title="Remove from project"
                              >
                                <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                  <path strokeLinecap="round" strokeLinejoin="round" d="M22 12h-4l-3 9L9 3l-3 9H2" />
                                </svg>
                              </button>
                            )}
                          </div>
                        )}
                      </div>
                    );
                  })}
                </div>
              )}
            </div>

            {/* Pending invitations */}
            {canManage && (
              <div className="card overflow-hidden">
                <div className="px-5 py-4 border-b flex items-center justify-between" style={{ borderColor: 'rgba(255,255,255,0.06)' }}>
                  <h2 className="text-sm font-semibold" style={{ color: 'var(--text)' }}>
                    Pending invitations
                  </h2>
                  <span className="text-xs px-2 py-0.5 rounded-full"
                        style={{ background: 'rgba(255,255,255,0.05)', color: 'var(--text-muted)' }}>
                    {pendingInvites.length}
                  </span>
                </div>

                {pendingInvites.length === 0 ? (
                  <div className="p-8 text-center">
                    <p className="text-xs" style={{ color: 'var(--text-subtle)' }}>No pending invitations</p>
                  </div>
                ) : (
                  <div className="divide-y" style={{ borderColor: 'rgba(255,255,255,0.04)' }}>
                    {pendingInvites.map(inv => (
                      <div key={inv.id}
                           className="flex items-center gap-4 px-5 py-4">

                        {/* Icon */}
                        <div className="w-9 h-9 rounded-full flex items-center justify-center shrink-0"
                             style={{ background: 'rgba(99,102,241,0.08)' }}>
                          <svg className="w-4 h-4" style={{ color: '#818CF8' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                            <path strokeLinecap="round" strokeLinejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" />
                          </svg>
                        </div>

                        {/* Info */}
                        <div className="flex-1 min-w-0">
                          <div className="flex items-center gap-2">
                            <span className="text-sm font-medium" style={{ color: 'var(--text)' }}>{inv.email}</span>
                            <span className={`badge ${ROLE_COLORS[inv.role] ?? 'badge-muted'}`}>
                              {ROLE_LABELS[inv.role] ?? inv.role}
                            </span>
                          </div>
                          <div className="flex items-center gap-3 mt-0.5 text-xs" style={{ color: 'var(--text-subtle)' }}>
                            <span>Sent {new Date(inv.createdAt).toLocaleDateString()}</span>
                            <span>·</span>
                            <span>Expires {new Date(inv.expiresAt).toLocaleDateString()}</span>
                          </div>
                        </div>

                        {/* Actions */}
                        <div className="flex items-center gap-1.5 shrink-0">
                          <button
                            onClick={() => handleCopyLink(inv)}
                            className="btn btn-secondary btn-sm"
                            title="Copy invite link"
                          >
                            {copiedInviteId === inv.id ? (
                              <svg className="w-3.5 h-3.5" style={{ color: '#86EFAC' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                              </svg>
                            ) : (
                              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" />
                              </svg>
                            )}
                          </button>
                          <button
                            onClick={() => handleRevoke(inv.id)}
                            disabled={revokingId === inv.id}
                            className="btn btn-danger btn-sm"
                            title="Revoke invitation"
                          >
                            {revokingId === inv.id ? '…' : (
                              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
                              </svg>
                            )}
                          </button>
                        </div>
                      </div>
                    ))}
                  </div>
                )}

                {pendingInvites.length > 0 && (
                  <div className="px-5 py-3 border-t" style={{ borderColor: 'rgba(255,255,255,0.04)' }}>
                    <p className="text-xs" style={{ color: 'var(--text-subtle)' }}>
                      Invitation links expire after 7 days. Copy the link and send it manually, or ask the recipient to check their email.
                    </p>
                  </div>
                )}
              </div>
            )}
          </div>
        )}
      </div>

      {/* Delete asset confirm modal */}
      {confirmDelete && (
        <div className="fixed inset-0 z-50 flex items-center justify-center"
             style={{ background: 'rgba(0,0,0,0.7)' }}>
          <div className="card p-6 max-w-sm w-full mx-4 animate-scale-in">
            <div className="flex items-center gap-3 mb-4">
              <div className="w-10 h-10 rounded-full flex items-center justify-center shrink-0"
                   style={{ background: 'rgba(248,113,113,0.15)' }}>
                <svg className="w-5 h-5" style={{ color: '#F87171' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
                </svg>
              </div>
              <div>
                <h3 className="text-base font-semibold" style={{ color: 'var(--text)' }}>Delete video?</h3>
                <p className="text-xs mt-0.5 truncate max-w-[220px]" style={{ color: 'var(--text-muted)' }}>
                  "{confirmDelete.title}"
                </p>
              </div>
            </div>
            <p className="text-sm mb-5" style={{ color: 'var(--text-muted)' }}>
              This will permanently delete the video file, thumbnail, and all HLS segments. This action cannot be undone.
            </p>
            <div className="flex gap-3 justify-end">
              <button
                onClick={() => setConfirmDelete(null)}
                disabled={!!deletingId}
                className="btn btn-secondary btn-md"
              >
                Cancel
              </button>
              <button
                onClick={confirmDeleteAsset}
                disabled={!!deletingId}
                className="btn btn-danger btn-md"
              >
                {deletingId === confirmDelete.id ? 'Deleting…' : 'Delete video'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Remove member confirm modal */}
      {confirmRemove && (
        <div className="fixed inset-0 z-50 flex items-center justify-center"
             style={{ background: 'rgba(0,0,0,0.7)' }}>
          <div className="card p-6 max-w-sm w-full mx-4 animate-scale-in">
            <h3 className="text-base font-semibold mb-2" style={{ color: 'var(--text)' }}>
              Remove {confirmRemove.name}?
            </h3>
            <p className="text-sm mb-5" style={{ color: 'var(--text-muted)' }}>
              They'll lose access to this project and all its videos. They can rejoin if invited again.
            </p>
            <div className="flex gap-3 justify-end">
              <button onClick={() => setConfirmRemove(null)} className="btn btn-secondary btn-md">
                Cancel
              </button>
              <button
                onClick={handleRemoveMember}
                disabled={removing}
                className="btn btn-danger btn-md"
              >
                {removing ? 'Removing…' : 'Remove'}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
